这篇博客讨论 Python 的_
字符,我们将看到,和 Python 中很多东西一样,下划线很多用法在大多数情况(并不绝对)下是一种惯例。
单一下划线(_
)
有典型的3种情况:
在解释器中:
_
存放的是交互式解释器会话中上一次执行的语句的结果。这个做法是标准 CPython 解释器最先采用的,其他的解释器也跟着采用了这种方式。1
2
3
4
5
6
7
8
9
10
11>>> _
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name '_' is not defined
>>> 42
>>> _
42
>>> 'alright!' if _ else ':('
'alright!'
>>> _
'alright!'作为名称(比如
_shahriar
): 这与上面那点有点联系,_
作为临时性的名称使用,别人在读你的代码的时候就会知道,你分配了一个特定的名字,但之后不会再用。举个例子,你对下面循环中的实际值不感兴趣:1
2
3n = 42
for _ in range(n):
do_something()国际化:也许你看过
_
被当做函数使用,这种情况,通常用于实现国际化和本地化字符串之间的对应关系查找,这个似乎是源自、遵循相应的 C 语言惯例。
例如,在Diango 翻译文档,你可以看到:1
2
3
4
5
6from django.utils.translation import ugettext as _
from django.http import HttpResponse
def my_view(request):
output = _("Welcome to my site.")
return HttpResponse(output)
可以看到,第2点和第3点是可能冲突的,我们应该避免在国际化查找过程的代码块中同时使用_
作为临时名称。
名称前的单下划线(如:_shahriar
)
对于程序员来说,名称以单一下划线开头,表明这个名称属性为私有。这个貌似是惯例,它让其他人(或自己)看到这些代码时知道以_
开头的变量是用于内部使用的。正如 Python 文档中所述:
以
_
为前缀的名称(如_spam
)应该被视为 API 中非公开的部分(不管是函数、方法还是数据成员)。此时,应该将它们看做实现细节,修改时无需对外部通知。
这是一种惯例,因为它对解释器来说确实有一定意义,如果你 from <module/package> import *
,以_
开头的名称不会被导入,除非模块或包的__all__
列表显式地写明了。可以看看import * in Python获得更多信息。
名称前的双下划线(如: __shahriar
)
名称(特指方法名)前使用双下划线(__
)并不是一种惯例,对解释器来说它有特定的意义。Python 修饰这些名称用来避免与子类定义的名称冲突,Python 文档指出,__spam
这种形式(至少两个前导下划线,最多一个后续下划线)的任何标示符会被_classname__spam
这种形式替换,在这里_classname
是去掉前缀下划线的当前类名,例如:
1 | >>> class A(object): |
正如所预料的,_internal_use
并未改变,而__method_name
被修饰为_ClassName__method_name
,此时,如果你创建一个 A 的子类 B(呃,烂名字,烂名字,烂名字),你就不能轻易重载 A 类的 __method_name
方法了:
1 | >>> class B(A): |
这个行为几乎和 Java 的 final 方法以及 C++ 的标准方法(非虚方法)一样。
名称前后的双下划线(如:__init__
)
这是 Python 的特殊方法名,其实这是一个惯例,对于 Python 系统来说,它可以确保不会与用户定义的名称冲突,通常情况下,你需要覆盖这些方法,在里面实现你需要的功能,以便 Python 调用它们,例如当写一个类时,你通常需要定义__init__
方法。
没有什么可以阻止你编写你自己的特殊方法名(但,请不要这么做):
1 | >>> class C(object): |
尽量离这种方式的命名远一点,只让 Python 内部定义的特殊名称遵循这种方式。